{
 "cells": [
  {
   "metadata": {},
   "cell_type": "markdown",
   "source": "# 1、使用@tool的方式创建工具",
   "id": "e94cd3571ea2861f"
  },
  {
   "metadata": {
    "ExecuteTime": {
     "end_time": "2025-08-04T09:17:49.739754Z",
     "start_time": "2025-08-04T09:17:49.727755Z"
    }
   },
   "cell_type": "code",
   "source": [
    "from pydantic import BaseModel\n",
    "\n",
    "\n",
    "#定义一个函数\n",
    "\n",
    "def add_number(num1: int, num2: int) -> int:\n",
    "    \"\"\"计算两个数的和\"\"\"\n",
    "    return num1 + num2\n",
    "\n",
    "\n",
    "add_number(10, 20)"
   ],
   "id": "f11e1bd1b9bbeb2c",
   "outputs": [
    {
     "data": {
      "text/plain": [
       "30"
      ]
     },
     "execution_count": 1,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "execution_count": 1
  },
  {
   "metadata": {},
   "cell_type": "markdown",
   "source": "如何定义一个工具\n",
   "id": "7dc5c47409079df9"
  },
  {
   "metadata": {
    "ExecuteTime": {
     "end_time": "2025-08-04T09:20:45.934964Z",
     "start_time": "2025-08-04T09:20:45.922959Z"
    }
   },
   "cell_type": "code",
   "source": [
    "from langchain_core.tools import tool, StructuredTool\n",
    "\n",
    "\n",
    "@tool\n",
    "def add_number(num1: int, num2: int) -> int:\n",
    "    \"\"\"计算两个数的和\"\"\"\n",
    "    return num1 + num2\n",
    "\n",
    "\n",
    "print(f\"name = {add_number.name}\")\n",
    "print(f\"description = {add_number.description}\")\n",
    "print(f\"args = {add_number.args}\")\n",
    "print(f\"return_direct= {add_number.return_direct}\")"
   ],
   "id": "8cd78a056d7b49a8",
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "name = add_number\n",
      "description = 计算两个数的和\n",
      "args = {'num1': {'title': 'Num1', 'type': 'integer'}, 'num2': {'title': 'Num2', 'type': 'integer'}}\n",
      "return_direct= False\n"
     ]
    }
   ],
   "execution_count": 3
  },
  {
   "metadata": {
    "ExecuteTime": {
     "end_time": "2025-08-04T09:22:37.162682Z",
     "start_time": "2025-08-04T09:22:37.155683Z"
    }
   },
   "cell_type": "code",
   "source": [
    "from langchain_core.tools import tool\n",
    "\n",
    "\n",
    "#使用装饰器修改函数的一些属性信息\n",
    "\n",
    "@tool(name_or_callable=\"add_two_numbers\", description=\"计算两个整数的和\",\n",
    "      return_direct=True)\n",
    "def add_number(num1: int, num2: int) -> int:\n",
    "    \"\"\"计算两个数的和\"\"\"\n",
    "    return num1 + num2\n",
    "\n",
    "\n",
    "print(f\"name = {add_number.name}\")\n",
    "print(f\"description = {add_number.description}\")\n",
    "print(f\"args = {add_number.args}\")\n",
    "print(f\"return_direct= {add_number.return_direct}\")"
   ],
   "id": "68fe61f075083d17",
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "name = add_two_numbers\n",
      "description = 计算两个整数的和\n",
      "args = {'num1': {'title': 'Num1', 'type': 'integer'}, 'num2': {'title': 'Num2', 'type': 'integer'}}\n",
      "return_direct= True\n"
     ]
    }
   ],
   "execution_count": 4
  },
  {
   "metadata": {
    "ExecuteTime": {
     "end_time": "2025-08-04T09:25:10.288322Z",
     "start_time": "2025-08-04T09:25:10.270807Z"
    }
   },
   "cell_type": "code",
   "source": [
    "from pydantic import Field\n",
    "from langchain_core.tools import tool\n",
    "from pydantic.main import BaseModel\n",
    "\n",
    "\n",
    "class FieldInfo(BaseModel):\n",
    "    num1: int = Field(description=\"第1个参数\")\n",
    "    num2: int = Field(description=\"第2个参数\")\n",
    "\n",
    "\n",
    "#使用装饰器修改函数的一些属性信息\n",
    "@tool(name_or_callable=\"add_two_numbers\", description=\"计算两个整数的和\",\n",
    "      return_direct=True, args_schema=FieldInfo)\n",
    "def add_number(num1: int, num2: int) -> int:\n",
    "    \"\"\"计算两个数的和\"\"\"\n",
    "    return num1 + num2\n",
    "\n",
    "\n",
    "print(f\"name = {add_number.name}\")\n",
    "print(f\"description = {add_number.description}\")\n",
    "print(f\"args = {add_number.args}\")\n",
    "print(f\"return_direct= {add_number.return_direct}\")"
   ],
   "id": "a1244f549413e2d6",
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "name = add_two_numbers\n",
      "description = 计算两个整数的和\n",
      "args = {'num1': {'description': '第1个参数', 'title': 'Num1', 'type': 'integer'}, 'num2': {'description': '第2个参数', 'title': 'Num2', 'type': 'integer'}}\n",
      "return_direct= True\n"
     ]
    }
   ],
   "execution_count": 6
  },
  {
   "metadata": {},
   "cell_type": "markdown",
   "source": [
    "# 2、使用StructuredTool.from_function定义一个工具\n",
    "\n"
   ],
   "id": "a12dfba8022c0cfd"
  },
  {
   "metadata": {
    "ExecuteTime": {
     "end_time": "2025-08-04T09:29:49.301598Z",
     "start_time": "2025-08-04T09:29:49.294597Z"
    }
   },
   "cell_type": "code",
   "source": [
    "from langchain_core.tools.structured import StructuredTool\n",
    "\n",
    "class FieldInfo(BaseModel):\n",
    "    num1: int = Field(description=\"第1个参数\")\n",
    "    num2: int = Field(description=\"第2个参数\")\n",
    "\n",
    "def add_number(num1: int, num2: int) -> int:\n",
    "    \"\"\"计算两个数的和\"\"\"\n",
    "    return num1 + num2\n",
    "\n",
    "\n",
    "#创建工具的方式2\n",
    "tool = StructuredTool.from_function(\n",
    "    func=add_number,\n",
    "    name=\"add_two_number\",\n",
    "    description=\"计算两个整数的和\",\n",
    "    return_direct=True,\n",
    "    args_schema=FieldInfo,\n",
    ")\n",
    "\n",
    "print(f\"name = {tool.name}\")\n",
    "print(f\"description = {tool.description}\")\n",
    "print(f\"args = {tool.args}\")\n",
    "print(f\"return_direct= {tool.return_direct}\")"
   ],
   "id": "722affd18a45e790",
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "name = add_two_number\n",
      "description = 计算两个整数的和\n",
      "args = {'num1': {'description': '第1个参数', 'title': 'Num1', 'type': 'integer'}, 'num2': {'description': '第2个参数', 'title': 'Num2', 'type': 'integer'}}\n",
      "return_direct= True\n"
     ]
    }
   ],
   "execution_count": 9
  },
  {
   "metadata": {},
   "cell_type": "markdown",
   "source": "使用工具的应用举例",
   "id": "92165faf365adb8f"
  },
  {
   "metadata": {
    "ExecuteTime": {
     "end_time": "2025-08-04T09:33:16.580984Z",
     "start_time": "2025-08-04T09:33:13.856592Z"
    }
   },
   "cell_type": "code",
   "source": [
    "#1.导入相关依赖\n",
    "from langchain_community.tools import MoveFileTool\n",
    "from langchain_core.messages import HumanMessage\n",
    "from langchain_core.utils.function_calling import convert_to_openai_function\n",
    "from langchain_openai import ChatOpenAI\n",
    "import os\n",
    "import dotenv\n",
    "from langchain_openai import ChatOpenAI\n",
    "\n",
    "dotenv.load_dotenv()\n",
    "\n",
    "os.environ['OPENAI_API_KEY'] = os.getenv(\"OPENAI_API_KEY1\")\n",
    "os.environ['OPENAI_BASE_URL'] = os.getenv(\"OPENAI_BASE_URL\")\n",
    "\n",
    "# 2.定义LLM模型\n",
    "model =ChatOpenAI(model=\"gpt-4o-mini\",temperature=0)\n",
    "\n",
    "# 3.定义工具\n",
    "tools = [MoveFileTool()]\n",
    "\n",
    "# 4.将工具转换为函数\n",
    "functions = [convert_to_openai_function(t) for t in tools]\n",
    "\n",
    "# print(functions[0])\n",
    "\n",
    "# 4.模型使用函数\n",
    "message = model.invoke(\n",
    "    [HumanMessage(content=\"move file foo to bar\")],\n",
    "    functions=functions\n",
    ")\n",
    "\n",
    "print(message)"
   ],
   "id": "2617634992938d30",
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "content='' additional_kwargs={'function_call': {'arguments': '{\"source_path\":\"foo\",\"destination_path\":\"bar\"}', 'name': 'move_file'}, 'refusal': None} response_metadata={'token_usage': {'completion_tokens': 21, 'prompt_tokens': 74, 'total_tokens': 95, 'completion_tokens_details': {'accepted_prediction_tokens': 0, 'audio_tokens': 0, 'reasoning_tokens': 0, 'rejected_prediction_tokens': 0}, 'prompt_tokens_details': {'audio_tokens': 0, 'cached_tokens': 0}}, 'model_name': 'gpt-4o-mini-2024-07-18', 'system_fingerprint': 'fp_efad92c60b', 'id': 'chatcmpl-C0lrAc5APeGC7NOkTDNbycKwuf3W3', 'service_tier': None, 'finish_reason': 'function_call', 'logprobs': None} id='run--7faaa3ad-89e2-4ae5-b4f2-20fd4cd98902-0' usage_metadata={'input_tokens': 74, 'output_tokens': 21, 'total_tokens': 95, 'input_token_details': {'audio': 0, 'cache_read': 0}, 'output_token_details': {'audio': 0, 'reasoning': 0}}\n"
     ]
    }
   ],
   "execution_count": 10
  },
  {
   "metadata": {
    "ExecuteTime": {
     "end_time": "2025-08-04T09:34:27.001611Z",
     "start_time": "2025-08-04T09:34:25.550604Z"
    }
   },
   "cell_type": "code",
   "source": [
    "message = model.invoke(\n",
    "    [HumanMessage(content=\"今天的天气怎么样？\")],\n",
    "    functions=functions\n",
    ")\n",
    "\n",
    "print(message)"
   ],
   "id": "571abd3be69c7c1b",
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "content='抱歉，我无法提供实时天气信息。你可以通过天气预报网站或应用程序查看今天的天气情况。' additional_kwargs={'refusal': None} response_metadata={'token_usage': {'completion_tokens': 27, 'prompt_tokens': 74, 'total_tokens': 101, 'completion_tokens_details': {'accepted_prediction_tokens': 0, 'audio_tokens': 0, 'reasoning_tokens': 0, 'rejected_prediction_tokens': 0}, 'prompt_tokens_details': {'audio_tokens': 0, 'cached_tokens': 0}}, 'model_name': 'gpt-4o-mini-2024-07-18', 'system_fingerprint': 'fp_efad92c60b', 'id': 'chatcmpl-C0lsIvEu4TyYdukqySHsUED2STAx0', 'service_tier': None, 'finish_reason': 'stop', 'logprobs': None} id='run--dafca1cf-0470-455b-bab6-6dc773c2d524-0' usage_metadata={'input_tokens': 74, 'output_tokens': 27, 'total_tokens': 101, 'input_token_details': {'audio': 0, 'cache_read': 0}, 'output_token_details': {'audio': 0, 'reasoning': 0}}\n"
     ]
    }
   ],
   "execution_count": 11
  },
  {
   "metadata": {},
   "cell_type": "markdown",
   "source": "确认是否存在我们要调用的函数",
   "id": "f29e85c8ee88d4bf"
  },
  {
   "metadata": {
    "ExecuteTime": {
     "end_time": "2025-08-04T09:37:08.887644Z",
     "start_time": "2025-08-04T09:37:07.438846Z"
    }
   },
   "cell_type": "code",
   "source": [
    "message = model.invoke(\n",
    "    [HumanMessage(content=\"将本目录下的abc.txt文件移动到C:\\\\Users\\\\shkst\\\\Desktop\")],\n",
    "    functions=functions\n",
    ")\n",
    "\n",
    "print(message)"
   ],
   "id": "19827e8e677f6cc1",
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "content='' additional_kwargs={'function_call': {'arguments': '{\"source_path\":\"abc.txt\",\"destination_path\":\"C:\\\\\\\\Users\\\\\\\\shkst\\\\\\\\Desktop\\\\\\\\abc.txt\"}', 'name': 'move_file'}, 'refusal': None} response_metadata={'token_usage': {'completion_tokens': 32, 'prompt_tokens': 87, 'total_tokens': 119, 'completion_tokens_details': {'accepted_prediction_tokens': 0, 'audio_tokens': 0, 'reasoning_tokens': 0, 'rejected_prediction_tokens': 0}, 'prompt_tokens_details': {'audio_tokens': 0, 'cached_tokens': 0}}, 'model_name': 'gpt-4o-mini-2024-07-18', 'system_fingerprint': 'fp_efad92c60b', 'id': 'chatcmpl-C0luuLqsjNNMJx9IuMpFcS1aPPmn8', 'service_tier': None, 'finish_reason': 'function_call', 'logprobs': None} id='run--14e3c7dd-a661-4aed-9681-f943faf4b2db-0' usage_metadata={'input_tokens': 87, 'output_tokens': 32, 'total_tokens': 119, 'input_token_details': {'audio': 0, 'cache_read': 0}, 'output_token_details': {'audio': 0, 'reasoning': 0}}\n"
     ]
    }
   ],
   "execution_count": 12
  },
  {
   "metadata": {
    "ExecuteTime": {
     "end_time": "2025-08-04T09:38:58.943372Z",
     "start_time": "2025-08-04T09:38:58.928368Z"
    }
   },
   "cell_type": "code",
   "source": [
    "import json\n",
    "\n",
    "if \"function_call\" in message.additional_kwargs:\n",
    "    tool_name = message.additional_kwargs[\"function_call\"][\"name\"]\n",
    "    tool_args = json.loads(message.additional_kwargs[\"function_call\"][\"arguments\"])\n",
    "    print(f\"调用工具: {tool_name}, 参数: {tool_args}\")\n",
    "else:\n",
    "    print(\"模型回复:\", message.content)"
   ],
   "id": "b250fd5f3b73e71",
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "调用工具: move_file, 参数: {'source_path': 'abc.txt', 'destination_path': 'C:\\\\Users\\\\shkst\\\\Desktop\\\\abc.txt'}\n"
     ]
    }
   ],
   "execution_count": 13
  },
  {
   "metadata": {
    "ExecuteTime": {
     "end_time": "2025-08-04T09:39:55.634402Z",
     "start_time": "2025-08-04T09:39:55.614942Z"
    }
   },
   "cell_type": "code",
   "source": [
    "from langchain.tools import MoveFileTool\n",
    "\n",
    "if \"move_file\" in message.additional_kwargs[\"function_call\"][\"name\"]:\n",
    "    tool = MoveFileTool()\n",
    "    result = tool.run(tool_args)  # 执行工具\n",
    "    print(\"工具执行结果:\", result)"
   ],
   "id": "afe6eafb7f036aca",
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "工具执行结果: File moved successfully from abc.txt to C:\\Users\\shkst\\Desktop\\abc.txt.\n"
     ]
    }
   ],
   "execution_count": 14
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 2
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython2",
   "version": "2.7.6"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
